package yunjiao.designpatterns.combinator;

/**
 * 解析器应用
 *
 * @author yangyunjiao
 */
public class UsageCombinators {
    // 解析整数
    public static Parser<Integer> integer() {
        return Combinators.many1(BasicParsers.digit())
                .map(digits -> {
                    String numberStr = digits.stream()
                            .map(String::valueOf)
                            .reduce("", String::concat);
                    return Integer.parseInt(numberStr);
                });
    }

    // 解析算术表达式
    public static Parser<Expr> expression() {
        return term().flatMap(firstTerm -> {
            Parser<Expr> continuation = Combinators.many(
                    BasicParsers.whitespace()
                            .flatMap(ws -> BasicParsers.character('+').or(BasicParsers.character('-')))
                            .flatMap(op -> BasicParsers.whitespace()
                                    .flatMap(ws2 -> term().map(term -> new Pair<>(op, term))))
            ).map(operations -> {
                Expr result = firstTerm;
                for (Pair<Character, Expr> operation : operations) {
                    if (operation.first == '+') {
                        result = new Expr.AddExpr(result, operation.second);
                    } else {
                        result = new Expr.SubtractExpr(result, operation.second);
                    }
                }
                return result;
            });
            return continuation;
        });
    }

    private static Parser<Expr> term() {
        return factor().flatMap(firstFactor -> {
            Parser<Expr> continuation = Combinators.many(
                    BasicParsers.whitespace()
                            .flatMap(ws -> BasicParsers.character('*').or(BasicParsers.character('/')))
                            .flatMap(op -> BasicParsers.whitespace()
                                    .flatMap(ws2 -> factor().map(factor -> new Pair<>(op, factor))))
            ).map(operations -> {
                Expr result = firstFactor;
                for (Pair<Character, Expr> operation : operations) {
                    if (operation.first == '*') {
                        result = new Expr.MultiplyExpr(result, operation.second);
                    } else {
                        result = new Expr.DivideExpr(result, operation.second);
                    }
                }
                return result;
            });
            return continuation;
        });
    }

    private static Parser<Expr> factor() {
        // 数字因子
        Parser<Expr> numberFactor = integer().map(Expr.NumberExpr::new);

        // 括号表达式因子
        Parser<Expr> parenFactor = input -> {
            ParseResult<Character> openResult = BasicParsers.character('(').parse(input);
            if (!openResult.success) {
                return ParseResult.failure(openResult.error);
            }

            ParseResult<Expr> exprResult = expression().parse(openResult.remaining);
            if (!exprResult.success) {
                return ParseResult.failure(exprResult.error);
            }

            ParseResult<Character> closeResult = BasicParsers.character(')').parse(exprResult.remaining);
            if (!closeResult.success) {
                return ParseResult.failure(closeResult.error);
            }

            return ParseResult.success(exprResult.value, closeResult.remaining);
        };

        return numberFactor.or(parenFactor);
    }

    record Pair<A, B>(A first, B second) {
    }
}
